package com.thouiot.modbus.service.impl;

import com.serotonin.modbus4j.ModbusMaster;
import com.serotonin.modbus4j.code.DataType;
import com.serotonin.modbus4j.locator.NumericLocator;
import com.thouiot.common.exception.TiotRuntimeException;
import com.thouiot.modbus.config.ModbusTypeEnum;
import com.thouiot.modbus.entity.vo.ModbusLocatorVo;
import com.thouiot.modbus.entity.vo.ReadValueVo;
import com.thouiot.modbus.entity.vo.WriteValueVo;
import com.thouiot.modbus.enums.ReadValueTypeEnum;
import com.thouiot.modbus.enums.RegisterTypeEnum;
import com.thouiot.modbus.enums.WriteValueTypeEnum;
import com.thouiot.modbus.exception.ModbusErrorCodeEnum;
import com.thouiot.modbus.component.ModbusMasterHolder;
import com.thouiot.modbus.service.IMasterService;
import com.thouiot.modbus.util.ModbusConvertUtil;
import com.thouiot.modbus.util.ModbusMasterUtil;
import com.thouiot.modbus.util.ModbusParamsUtil;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;

/**
 * @description modbus 服务实现类
 * @author wangkai
 * @date 2025/04/11 09:56
 */
@Service
public class MasterServiceImpl implements IMasterService {

    @Resource
    private ModbusMasterHolder modbusMasterHolder;

    @Override
    public Boolean readModbusStatus(ModbusLocatorVo readModbusVo) {
        ReadValueVo readValueVo = readModbus(readModbusVo);
        Object value = readValueVo.getValue();
        if (value instanceof Boolean) {
            return (Boolean) value;
        }
        return null;
    }

    @Override
    public Number readModbusRegister(ModbusLocatorVo readModbusVo) {
        ReadValueVo readValueVo = readModbus(readModbusVo);
        Object value = readValueVo.getValue();
        if (value instanceof Number) {
            return (Number) value;
        }
        return null;
    }

    @Override
    public short[] readModbusRegisters(ModbusLocatorVo readModbusVo) {
        ReadValueVo readValueVo = readModbus(readModbusVo);
        return (short[]) readValueVo.getValue();
    }

    @Override
    public ReadValueVo readModbus(ModbusLocatorVo readModbusVo) {
        ModbusParamsUtil.checkBaseParams(readModbusVo);
        ReadValueVo readValue = readModbusVo.getReadValue();
        String registerType = readModbusVo.getRegisterType();
        String readValueType = readValue.getType();
        ModbusParamsUtil.checkRegisterReadType(registerType, readValueType);

        ModbusTypeEnum masterType = ModbusTypeEnum.getModbusTypeEnum(readModbusVo.getModbusType());
        ModbusMaster modbusMaster = modbusMasterHolder.getModbusMaster(readModbusVo.getName(), masterType);
        if (modbusMaster == null) {
            throw new TiotRuntimeException(ModbusErrorCodeEnum.MODBUS_MASTER_NOT_FOUND);
        }

        ReadValueVo readValueVo = new ReadValueVo();
        try {
            if (ReadValueTypeEnum.BOOLEAN.getCode().equals(readValueType)) {
                if (RegisterTypeEnum.COIL_INPUT.getCode().equals(registerType)) {
                    readValueVo.setValue(ModbusMasterUtil.readCoilStatus(modbusMaster, readModbusVo.getSlaveId(), readModbusVo.getOffset()));
                } else if (RegisterTypeEnum.DISCRETE_INPUT.getCode().equals(registerType)) {
                    readValueVo.setValue(ModbusMasterUtil.readInputStatus(modbusMaster, readModbusVo.getSlaveId(), readModbusVo.getOffset()));
                } else {
                    throw new TiotRuntimeException(ModbusErrorCodeEnum.REGISTER_TYPE_ERROR);
                }
            } else if (ReadValueTypeEnum.NUMBER.getCode().equals(readValueType)) {
                if (RegisterTypeEnum.HOLDING_REGISTER.getCode().equals(registerType)) {
                    readValueVo.setValue(ModbusMasterUtil.readHoldingRegister(modbusMaster, readModbusVo.getSlaveId(), readModbusVo.getOffset(), readValue.getDataType()));
                } else if (RegisterTypeEnum.INPUT_REGISTER.getCode().equals(registerType)) {
                    readValueVo.setValue(ModbusMasterUtil.readInputRegister(modbusMaster, readModbusVo.getSlaveId(), readModbusVo.getOffset(), readValue.getDataType()));
               } else {
                    throw new TiotRuntimeException(ModbusErrorCodeEnum.REGISTER_TYPE_ERROR);
                }
            } else if (ReadValueTypeEnum.NUMBERS.getCode().equals(readValueType)) {
                if (RegisterTypeEnum.HOLDING_REGISTER.getCode().equals(registerType)) {
                    int registerCount = ModbusConvertUtil.getRegisterCount(readValue.getDataType());
                    int shortCount  = registerCount * readValue.getLen();
                    byte[] bytes = ModbusMasterUtil.readHoldingRegisters(modbusMaster, readModbusVo.getSlaveId(), readModbusVo.getOffset(), shortCount);
                    List<Number> numbers = ModbusConvertUtil.convertToNumbers(bytes, readValue.getDataType());
                    readValueVo.setValue(numbers);
                } else if (RegisterTypeEnum.INPUT_REGISTER.getCode().equals(registerType)) {
                    int registerCount = ModbusConvertUtil.getRegisterCount(readValue.getDataType());
                    int shortCount  = registerCount * readValue.getLen();
                    byte[] bytes = ModbusMasterUtil.readInputRegisters(modbusMaster, readModbusVo.getSlaveId(), readModbusVo.getOffset(), shortCount);
                    List<Number> numbers = ModbusConvertUtil.convertToNumbers(bytes, readValue.getDataType());
                    readValueVo.setValue(numbers);
                }
            }
            // TODO 因为写入寄存器时modbus4j 没有提供 byte 写入，为了保持一致暂不实现 byte读取
            // TODO 暂不支持 byte 读取
            // TODO 参考 com.serotonin.modbus4j.msg.WriteCoilsRequest，com.serotonin.modbus4j.msg.com.serotonin.modbus4j.msg.WriteRegistersRequest，boolean[] 和 short[] 都是转换为 byte[] 存储的
            // TODO 故也可以使用byte[] 转换为short[]，再使用 com.serotonin.modbus4j.msg.WriteRegistersRequest 进行写入操作
//            else if (ReadValueTypeEnum.BYTES.getCode().equals(readValueType)) {
//                if (RegisterTypeEnum.HOLDING_REGISTER.getCode().equals(registerType)) {
//                    readValueVo.setBytes(ModbusMasterUtil.readHoldingRegisters(modbusMaster, readModbusVo.getSlaveId(), readModbusVo.getOffset(), readValue.getLen()));
//                } else if (RegisterTypeEnum.INPUT_REGISTER.getCode().equals(registerType)) {
//                    readValueVo.setBytes(ModbusMasterUtil.readInputRegisters(modbusMaster, readModbusVo.getSlaveId(), readModbusVo.getOffset(), readValue.getLen()));
//                } else {
//                    throw new TiotRuntimeException(ModbusErrorCodeEnum.REGISTER_TYPE_ERROR);
//                }
//            }
            else if (ReadValueTypeEnum.SHORTS.getCode().equals(readValueType)) {
                if (RegisterTypeEnum.HOLDING_REGISTER.getCode().equals(registerType)) {
                    byte[] bytes = ModbusMasterUtil.readHoldingRegisters(modbusMaster, readModbusVo.getSlaveId(), readModbusVo.getOffset(), readValue.getLen());
                    readValueVo.setValue(bytes == null ? null : ModbusConvertUtil.convertToShorts(bytes));
                } else if (RegisterTypeEnum.INPUT_REGISTER.getCode().equals(registerType)) {
                    byte[] bytes = ModbusMasterUtil.readInputRegisters(modbusMaster, readModbusVo.getSlaveId(), readModbusVo.getOffset(), readValue.getLen());
                    readValueVo.setValue(bytes == null ? null : ModbusConvertUtil.convertToShorts(bytes));
                } else {
                    throw new TiotRuntimeException(ModbusErrorCodeEnum.REGISTER_TYPE_ERROR);
                }
            } else if (ReadValueTypeEnum.BOOLEANS.getCode().equals(readValueType)) {
                if (RegisterTypeEnum.COIL_INPUT.getCode().equals(registerType)) {
                    boolean[] booleans = ModbusMasterUtil.readCoilStatus(modbusMaster, readModbusVo.getSlaveId(), readModbusVo.getOffset(), readValue.getLen());
                    readValueVo.setValue(booleans);
                } else if (RegisterTypeEnum.DISCRETE_INPUT.getCode().equals(registerType)) {
                    boolean[] booleans = ModbusMasterUtil.readInputStatus(modbusMaster, readModbusVo.getSlaveId(), readModbusVo.getOffset(), readValue.getLen());
                    readValueVo.setValue(booleans);
                } else {
                    throw new TiotRuntimeException(ModbusErrorCodeEnum.REGISTER_TYPE_ERROR);
                }
            } else if (ReadValueTypeEnum.SHORT.getCode().equals(readValueType)) {
                if (RegisterTypeEnum.HOLDING_REGISTER.getCode().equals(registerType)) {
                    readValueVo.setValue(ModbusMasterUtil.readHoldingRegister(modbusMaster, readModbusVo.getSlaveId(), readModbusVo.getOffset(), DataType.TWO_BYTE_INT_UNSIGNED));
                } else if (RegisterTypeEnum.INPUT_REGISTER.getCode().equals(registerType)) {
                    readValueVo.setValue(ModbusMasterUtil.readInputRegister(modbusMaster, readModbusVo.getSlaveId(), readModbusVo.getOffset(), DataType.TWO_BYTE_INT_UNSIGNED));
                }
            } else {
                throw new TiotRuntimeException(ModbusErrorCodeEnum.READ_VALUE_TYPE_ERROR);
            }
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        return readValueVo;
    }

    @Override
    public boolean writeModbus(ModbusLocatorVo readModbusVo) {
        ModbusParamsUtil.checkBaseParams(readModbusVo);
        WriteValueVo writeValue = readModbusVo.getWriteValue();
        if (writeValue == null) {
            return false;
        }
        String type = writeValue.getType();
        Object value = writeValue.getValue();
        WriteValueTypeEnum valueTypeEnum = WriteValueTypeEnum.getWriteValueTypeEnum(type);
        ModbusParamsUtil.checkValueTypeMatchValue(valueTypeEnum, value);

        RegisterTypeEnum registerTypeEnum = RegisterTypeEnum.getRegisterTypeEnum(readModbusVo.getRegisterType());
        ModbusParamsUtil.checkRegisterTypeMatchValueType(registerTypeEnum, valueTypeEnum);

        ModbusMaster master = modbusMasterHolder.getModbusMaster(readModbusVo.getName(), ModbusTypeEnum.getModbusTypeEnum(readModbusVo.getModbusType()));
        if (master == null) {
            throw new TiotRuntimeException(ModbusErrorCodeEnum.MODBUS_MASTER_NOT_FOUND);
        }
        try {
            if (WriteValueTypeEnum.BOOLEAN.getCode().equals(type)) {
                return ModbusMasterUtil.writeCoil(master, readModbusVo.getSlaveId(), readModbusVo.getOffset(), (boolean) value);
            } else if (WriteValueTypeEnum.BOOLEANS.getCode().equals(type)) {
                List<Boolean> list = (List<Boolean>) value;
                return ModbusMasterUtil.writeCoils(master, readModbusVo.getSlaveId(), readModbusVo.getOffset(), ModbusConvertUtil.convertToBooleans(list));
            } else if (WriteValueTypeEnum.SHORT.getCode().equals(type)) {
                return ModbusMasterUtil.writeRegister(master, readModbusVo.getSlaveId(), readModbusVo.getOffset(), (int) value);
            } else if (WriteValueTypeEnum.SHORTS.getCode().equals(type)) {
                List<Integer> list = (List<Integer>) value;
                return ModbusMasterUtil.writeRegisters(master, readModbusVo.getSlaveId(), readModbusVo.getOffset(), ModbusConvertUtil.convertToShorts(list));
            } else if (WriteValueTypeEnum.NUMBER.getCode().equals(type)) {
                if (writeValue.getDataType() == null) {
                    throw new TiotRuntimeException(ModbusErrorCodeEnum.DATA_TYPE_EMPTY_ERROR);
                }
                return ModbusMasterUtil.writeHoldingRegister(master, readModbusVo.getSlaveId(), readModbusVo.getOffset(), (Number) value, writeValue.getDataType());
            } else if (WriteValueTypeEnum.NUMBERS.getCode().equals(type)) {
                List<Number> list = (List<Number>) value;
                return ModbusMasterUtil.writeHoldingRegisters(master, readModbusVo.getSlaveId(), readModbusVo.getOffset(), list, writeValue.getDataType());
            } else {
                throw new TiotRuntimeException(ModbusErrorCodeEnum.REGISTER_TYPE_ERROR);
            }
        } catch (Exception e) {
            e.printStackTrace();
            throw new TiotRuntimeException(ModbusErrorCodeEnum.WRITE_MODBUS_ERROR);
        }
    }


}
